import { memo, useRef, useEffect } from 'react'
import { useUpdateEffect } from 'ahooks'
import { bind, clear } from 'size-sensor'
import { isString, isFunction } from 'loadsh'

const EChartsCore = memo((props) => {
  const echarts = props.echarts
  const ele = useRef(null)
  const isInitialResize = useRef(true)

  useEffect(() => {
    renderNewEcharts()
    return () => {
      dispose()
    }
  }, [])

  const { theme, opts, onEvents } = props
  useUpdateEffect(() => {
    dispose()
    renderNewEcharts()
  }, [theme, opts, onEvents])

  const { option, notMerge, lazyUpdate, showLoading, loadingOption } = props
  useUpdateEffect(() => {
    updateEChartsOption()
  }, [option, notMerge, lazyUpdate, showLoading, loadingOption])

  const { style, className } = props
  useUpdateEffect(() => {
    resize()
  }, [style, className])

  const getEchartsInstance = () => {
    return echarts.getInstanceByDom(ele.current)
  }

  const initEchartsInstance = () => {
    return new Promise((resolve) => {
      echarts.init(ele.current, props.theme, props.opts)
      const echartsInstance = getEchartsInstance()

      echartsInstance.on('finished', () => {
        const width = ele.current.clientWidth
        const height = ele.current.clientHeight

        echarts.dispose(ele.current)

        const opts = {
          width,
          height,
          ...props.opts
        }
        resolve(echarts.init(ele.current, props.theme, opts))
      })
    })
  }

  const dispose = () => {
    if (ele.current) {
      try {
        clear(ele.current)
      } catch (e) {
        console.warn(e)
      }
      echarts.dispose(ele.current)
    }
  }

  const renderNewEcharts = async () => {
    const { onEvents, onChartReady } = props

    await initEchartsInstance()

    const echartsInstance = updateEChartsOption()

    bindEvents(echartsInstance, onEvents || {})

    if (isFunction(onChartReady)) onChartReady(echartsInstance)

    if (ele.current) {
      bind(ele.current, () => {
        resize()
      })
    }
  }

  const bindEvents = (instance, events) => {
    const _bindEvent = (eventName, func) => {
      if (isString(eventName) && isFunction(func)) {
        instance.on(eventName, (param) => {
          func(param, instance)
        })
      }
    }

    for (const eventName in events) {
      if (Object.prototype.hasOwnProperty.call(events, eventName)) {
        _bindEvent(eventName, events[eventName])
      }
    }
  }

  const updateEChartsOption = () => {
    const {
      option,
      notMerge = false,
      lazyUpdate = false,
      showLoading,
      loadingOption = null
    } = props
    const echartInstance = getEchartsInstance()
    echartInstance.setOption(option, notMerge, lazyUpdate)
    if (showLoading) {
      echartInstance.showLoading(loadingOption)
    } else {
      echartInstance.hideLoading()
    }
    return echartInstance
  }

  const resize = () => {
    const echartsInstance = getEchartsInstance()

    if (!isInitialResize.current) {
      try {
        echartsInstance.resize({
          width: 'auto',
          height: 'auto'
        })
      } catch (e) {
        console.warn(e)
      }
    }

    isInitialResize.current = false
  }

  const newStyle = { height: 300, ...style }

  return <div ref={ele} style={newStyle} className={`bi-echarts ${className || ''}`} />
})

export default EChartsCore
